package dice.sinanya.dice.system;

import dice.sinanya.dice.MakeNickToSender;
import dice.sinanya.entity.EntityLogTag;
import dice.sinanya.entity.EntityLogText;
import dice.sinanya.entity.EntityTypeMessages;
import dice.sinanya.entity.imal.LogType;
import dice.sinanya.exceptions.CantEmptyLogNameException;
import dice.sinanya.exceptions.CantInPrivateException;
import dice.sinanya.exceptions.LogGroupGetException;
import dice.sinanya.tools.log.SaveDocx;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.nlpcn.commons.lang.util.StringUtil;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.*;

import static dice.sinanya.system.MessagesLog.LOG_NAME_FOR_GROUP;
import static dice.sinanya.system.MessagesLog.LOG_SWITCH_FOR_GROUP;
import static dice.sinanya.system.MessagesLogGetLock.LOG_GET_LOCK;
import static dice.sinanya.system.MessagesSystem.NONE;
import static dice.sinanya.system.MessagesSystem.SPACE;
import static dice.sinanya.system.MessagesTag.*;
import static dice.sinanya.tools.checkdata.CheckCantInPrivate.checkCantInPrivate;
import static dice.sinanya.tools.getinfo.GetMessagesProperties.entitySystemProperties;
import static dice.sinanya.tools.getinfo.GetNickName.getGroupName;
import static dice.sinanya.tools.getinfo.LogTag.*;
import static dice.sinanya.tools.getinfo.LogText.getLogText;
import static dice.sinanya.tools.log.LogSave.*;
import static dice.sinanya.tools.log.SendMail.sendMail;
import static dice.sinanya.tools.makedata.MakeMessages.deleteTag;
import static dice.sinanya.tools.makedata.Sender.sender;
import static dice.sinanya.tools.makedata.StringFormatById.stringFormatById;

/**
 * @author SitaNya
 * 日期: 2019-06-15
 * 电子邮箱: sitanya@qq.com
 * 维护群(QQ): 162279609
 * 有任何问题欢迎咨询
 * 类说明: 日志记录类
 */
public class Log implements MakeNickToSender {

    private final EntityTypeMessages entityTypeMessages;

    public Log(EntityTypeMessages entityTypeMessages) {
        this.entityTypeMessages = entityTypeMessages;
    }

    private static void testTask(ExecutorService exec, EntityTypeMessages entityTypeMessages, String msg, ArrayList<EntityLogText> bigResult) throws Docx4JException {
        SaveToDocxByFuture(exec, entityTypeMessages, msg, bigResult);
    }

    private static void SaveDocxNotHide(ExecutorService exec, EntityTypeMessages entityTypeMessages, String msg, ArrayList<EntityLogText> bigResult) throws Docx4JException {
        ArrayList<EntityLogText> text = new ArrayList<>();
        for (EntityLogText entityLogText : bigResult) {
            if (entityLogText.getLogType() != LogType.HIDE) {
                text.add(entityLogText);
            }
        }
        SaveToDocxByFuture(exec, entityTypeMessages, msg, bigResult);
    }

    private static void SaveToDocxByFuture(ExecutorService exec, EntityTypeMessages entityTypeMessages, String msg, ArrayList<EntityLogText> bigResult) throws Docx4JException {
        SaveDocx saveDoc = new SaveDocx(entityTypeMessages.getFromGroupString(), msg, bigResult);
        Future<Boolean> future = exec.submit(saveDoc);
        Boolean taskResult = null;
        String failReason = null;
        try {
            // 等待计算结果，最长等待timeout秒，timeout秒后中止任务
            taskResult = future.get(60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            failReason = "主线程在等待计算结果时被中断！";
        } catch (ExecutionException e) {
            failReason = "主线程等待计算结果，但计算抛出异常！";
        } catch (TimeoutException e) {
            failReason = "主线程等待计算结果超时，因此中断任务线程！";
            sender(entityTypeMessages, "日志过大，染色线程超过10分钟未能完成，已终止。请联系骰主取docx染色日志，或者换用染色网站");
            exec.shutdownNow();
        }

        System.out.println("\ntaskResult : " + taskResult);
        System.out.println("failReason : " + failReason);
    }

    /**
     * 记录开启，若已存在则重新开启日志进行追加，若不存在则关闭日志
     */
    public void logOn() throws CantInPrivateException, CantEmptyLogNameException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_ON;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));
        checkLogName(msg);
        if (checkOthorLogTrue(entityTypeMessages.getFromGroupString())) {
            if (getOtherLogTrue(entityTypeMessages.getFromGroupString()) != null) {
                sender(entityTypeMessages, stringFormatById(entitySystemProperties.getAlreadyOpen(entityTypeMessages.getFromGroupString()), getOtherLogTrue(entityTypeMessages.getFromGroupString())));
            } else {
                logOff();
            }
        } else {
            if (checkLogTagExist(entityTypeMessages, msg)) {
                sender(entityTypeMessages, stringFormatById(entitySystemProperties.getAppendLog(entityTypeMessages.getFromGroupString()), makeLogNickToSender(msg)));
            } else {
                sender(entityTypeMessages, stringFormatById(entitySystemProperties.getCreateLog(entityTypeMessages.getFromGroupString()), makeLogNickToSender(msg)));
            }
            LOG_NAME_FOR_GROUP.put(entityTypeMessages.getFromGroupString(), msg);
            LOG_SWITCH_FOR_GROUP.put(entityTypeMessages.getFromGroupString(), true);
            setLogTagSwitch(entityTypeMessages, msg, true);
        }
    }

    /**
     * 日志关闭
     */
    public void logOff() throws CantInPrivateException, CantEmptyLogNameException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_OFF;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));
//        checkLogName(msg);
        if (checkLogTagExist(entityTypeMessages, msg)) {
            if (checkLogTagSwitch(entityTypeMessages, msg)) {
                setLogTagSwitch(entityTypeMessages, msg, false);
                LOG_NAME_FOR_GROUP.remove(entityTypeMessages.getFromGroupString());
                LOG_SWITCH_FOR_GROUP.put(entityTypeMessages.getFromGroupString(), false);
                sender(entityTypeMessages, stringFormatById(entitySystemProperties.getLogOffDone(entityTypeMessages.getFromGroupString()), makeLogNickToSender(msg), msg));
            } else {
                String logName = getOtherLogTrue(entityTypeMessages.getFromGroupString());
                setLogTagSwitch(entityTypeMessages, logName, false);
                LOG_NAME_FOR_GROUP.remove(entityTypeMessages.getFromGroupString());
                LOG_SWITCH_FOR_GROUP.put(entityTypeMessages.getFromGroupString(), false);
                sender(entityTypeMessages, stringFormatById(entitySystemProperties.getAlreadyClose(entityTypeMessages.getFromGroupString()), makeLogNickToSender(msg)) + "\n现已帮您关闭当前开启的日志:\t" + logName);
            }
        } else {
            String logName = getOtherLogTrue(entityTypeMessages.getFromGroupString());
            setLogTagSwitch(entityTypeMessages, logName, false);
            LOG_NAME_FOR_GROUP.remove(entityTypeMessages.getFromGroupString());
            LOG_SWITCH_FOR_GROUP.put(entityTypeMessages.getFromGroupString(), false);
            sender(entityTypeMessages, String.format(entitySystemProperties.getNotFoundLog(entityTypeMessages.getFromGroupString()), msg) + "\n现已帮您关闭当前开启的日志:\t" + logName);
        }
    }

    /**
     * 日志get，无法get开启的日志
     */
    public void get() throws CantInPrivateException, Docx4JException, CantEmptyLogNameException, UnsupportedEncodingException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_GET;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));
        checkLogName(msg);
        if (!checkLogTagSwitch(entityTypeMessages, msg)) {
            getLogTextForSend(msg, NONE);
        } else {
            sender(entityTypeMessages, stringFormatById(entitySystemProperties.getLogGetForOpen(entityTypeMessages.getFromGroupString()), makeLogNickToSender(msg)));
        }
        System.gc();
    }

    public void getLogListForGroups() throws CantInPrivateException, CantEmptyLogNameException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_GROUP_LIST;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));
        checkLogName(msg);

        ArrayList<String> groupsId = getGroupsForLogName(msg, entityTypeMessages);
        sender(entityTypeMessages, StringUtil.joiner(groupsId, "\n"));
    }

    public void getLogForGroups() throws CantInPrivateException, CantEmptyLogNameException, UnsupportedEncodingException, Docx4JException, LogGroupGetException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_GROUP;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));

        if (msg.contains(SPACE) && msg.split(SPACE).length == 2) {
            if ("get".equals(msg.split(SPACE)[0])) {
                throw new LogGroupGetException(entityTypeMessages);
            }
            String logName = msg.split(SPACE)[1];
            checkLogName(logName);
            getLogTextForSend(logName, msg.split(SPACE)[0]);
        } else {
            throw new CantEmptyLogNameException(entityTypeMessages);
        }
        System.gc();
    }

    private void getLogTextForSend(String msg, String groups) throws UnsupportedEncodingException, Docx4JException {
        if (!checkLogTagExist(entityTypeMessages, msg)) {
            sender(entityTypeMessages, entitySystemProperties.getNotFoundLog(entityTypeMessages.getFromGroupString()));
            return;
        }
        if (LOG_GET_LOCK.contains(msg)) {
            sender(entityTypeMessages, entitySystemProperties.getReadLock(entityTypeMessages.getFromGroupString()));
        } else {
            LOG_GET_LOCK.add(msg);
        }
        final ArrayList<EntityLogText> bigResult;
        if (Objects.equals(groups, NONE)) {
            bigResult = getLogText(new EntityLogTag(entityTypeMessages.getFromGroupString(), msg));
        } else {
            bigResult = getLogText(msg, groups);
        }
        sender(entityTypeMessages, "正在抽取数据库为" + makeLogNickToSender(msg) + "生成原文件");
        logSave(entityTypeMessages.getFromGroupString(), msg, bigResult);
        if (bigResult.size() <= 524288 * 2) {
            sender(entityTypeMessages, "正在抽取数据库为" + makeLogNickToSender(msg) + "生成装饰后文件");
            logSaveOutPut(entityTypeMessages.getFromGroupString(), msg, bigResult);
            sender(entityTypeMessages, "正在抽取数据库为" + makeLogNickToSender(msg) + "生成去括号文件");
            logSaveNotHide(entityTypeMessages.getFromGroupString(), msg, bigResult);
        }
        if (bigResult.size() <= 524288) {
            ExecutorService exec = Executors.newCachedThreadPool();
            sender(entityTypeMessages, "正在抽取数据库为" + makeLogNickToSender(msg) + "生成染色文件");
            testTask(exec, entityTypeMessages, msg, bigResult);
            sender(entityTypeMessages, "正在抽取数据库为" + makeLogNickToSender(msg) + "生成去括号染色文件");
            SaveDocxNotHide(exec, entityTypeMessages, msg, bigResult);
            exec.shutdown();
        }
        sender(entityTypeMessages, makeLogNickToSender(msg) + "正在发送到您的邮箱" + entityTypeMessages.getFromQq() + "@qq.com");
        sendMail(entityTypeMessages.getFromQqString(), entityTypeMessages.getFromGroupString(), getGroupName(entityTypeMessages), msg);
        sender(entityTypeMessages, "[CQ:at,qq=" + entityTypeMessages.getFromQq() + "] 已发送到您的QQ邮箱，注意查收");
        LOG_GET_LOCK.remove(msg);
        bigResult.clear();
    }

    /**
     * 当前群内日志列表
     */
    public void list() throws CantInPrivateException {
        checkCantInPrivate(entityTypeMessages);
        sender(entityTypeMessages, getTagList(entityTypeMessages.getFromGroupString()));
    }

    /**
     * 删除日志
     */
    public void del() throws CantInPrivateException {
        checkCantInPrivate(entityTypeMessages);
        String tag = TAG_LOG_RM;
        String msg = deleteTag(entityTypeMessages.getMsgGet().getMsg(), tag.substring(0, tag.length() - 2));
        if (checkLogTagExist(entityTypeMessages, msg)) {
            if (checkLogTagSwitch(entityTypeMessages, msg)) {
                sender(entityTypeMessages, entitySystemProperties.getDeleteOpenLog(entityTypeMessages.getFromGroupString()));
            } else {
                delLog(new EntityLogTag(entityTypeMessages.getFromGroupString(), msg));
                sender(entityTypeMessages, "已删除日志: " + makeLogNickToSender(msg));
            }
        } else {
            sender(entityTypeMessages, "不存在日志: " + makeLogNickToSender(msg));
        }
    }

    private void checkLogName(String msg) throws CantEmptyLogNameException {
        if (msg.equals(NONE)) {
            throw new CantEmptyLogNameException(entityTypeMessages);
        }
    }
}
